iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 19
0
自我挑戰組

Metaprogramming Ruby and Rails系列 第 19

Day 19 -- Refinement

  • 分享至 

  • xImage
  •  

What is Refinement ?

還記得 Open Classes 可能會造成的問題嗎?當我們利用開放類別的特性,覆寫了類別內原有的方法,就會造成了全域性的改變。這代表改寫方法之後,任何物件呼叫該方法都受到影響。

class String
  def length
    size > 5 ? "long" : "short"
  end
end

"War and Peace".length     # => "long"
"War".length               # => "short"

不同於 Monkey patch,Ruby 黑魔法 Refinements 所能影響的範圍是區域性,而不是全域性的。
自從 Ruby 2.0之後,你可以使用 refine 以更安全的方式覆寫方法。 用法是先定義一個模組,並且在該模組內裡面放你要覆寫的類別及方法。
當需要 refinements 的時候,並不會自動啟用,你必須以關鍵字 using 才能開始使用。

module StringExtensions
  refine String do
    def length
      size > 5 ? "long" : "short"
    end
  end
end

using StringExtensions
p "War and Peace".length # => "long"
p "War".length # => "short"

Refinements 與 Monkey patches 類似,但影響範圍不是全域性的。Refinements 的作用域是有限的,且只能活在這兩個地方 :
(1) 自己本身的 refine block itself, 
(2) 從你開始呼叫 using 的程式碼為起點,一直到模組結束。那如果你是在最頂層呼叫 using 的話,就是一直到該檔案結束為止。

接著看以下範例:

class MyClass 
  def my_method
    "original my_method()"
  end
  def another_method 
    my_method
  end 
end

module MyClassRefinement 
  refine MyClass do
    def my_method
      "refined my_method()"
    end 
  end
end

self    # => "main", at the top level

using MyClassRefinement
MyClass.new.my_method      # => "refined my_method()"
MyClass.new.another_method # => "original my_method()"

你大概不會很驚訝當你看到 MyClass.new.my_method() 方法的回傳值是 " refined my_method() ",但是為什麼呼叫 another_method() 的回傳值竟然是 "original my_method()" 呢?
明明我們使用 using 是在 another_method() 之前啊!!!原因是在 MyClass 類別中, another_method()方法內呼叫 my_method() 方法是在 using 之前,所以 Ruby 會呼叫原始、未經修改過版本的my_method() 方法。

以上這樣的結果其實是違反一般直覺的反應,因此在使用 Refinement 時,最好重複檢查呼叫方法的順序流程,並且記得 Refinement 只能用在一般模組上,即使類別是繼承自模組,也不能用在類別上。

明天將介紹另外兩個可以取代 Around Aliases 的黑魔法:Refinement Wrapper & Prepended Wrapper


上一篇
Day 18 -- Around Aliases
下一篇
Day 20 -- Refinement Wrapper & Prepended Wrapper
系列文
Metaprogramming Ruby and Rails33
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言